/*--------------------------------------------------------------------------------
 * $B%U%l!<%`$4$H$K99?7$5$l$k;~7W(B
 *
 * ===Copyright===
 * Copyright (C) 2005-2006 toge (toge.mail@gmail.com)
 * (See Copyright Notice at COPYRIGHT file)
 *
 * ===Licence===
 * Mit License
 *
 * ===Revision===
 * $Revision: 203 $
 -------------------------------------------------------------------------------*/
#include "FrameClock.h"

#include <ctime>

#if (defined(unix) || defined(__unix__) || defined(_AIX) || defined(__OpenBSD__))
#include <sys/time.h>
#elif defined(WIN32)
#include <windows.h>
#else
#include <SDL.h>
#endif

namespace ggl2d
{

FrameTime FrameClock::initial_;
FrameTime FrameClock::span_;
FrameTime FrameClock::now_;
FrameTime FrameClock::change_;
FrameClock::Mode FrameClock::mode_;
double FrameClock::FastTime = 3.0;     /* $B9bB.F0:n;~$NG\B.EY(B */
double FrameClock::SlowTime = 1 / 5.0; /* $BDcB.F0:n;~$NDcB.EY(B */

#ifdef WIN32
double FrameClock::secondPerCounter_;
#endif

FrameTime
FrameClock::__current__()
{
#if (defined(unix) || defined(__unix__) || defined(_AIX) || defined(__OpenBSD__))
  timeval current;
  gettimeofday(&current, NULL);
  return FrameTime(current.tv_sec, current.tv_usec);
#elif defined(WIN32)
  LONGLONG current;
  QueryPerformanceCounter((LARGE_INTEGER *)&current);
  return FrameTime(current * secondPerCounter_);
#else
  Uint32 current = SDL_GetTicks();
  return FrameTime(current / 1000, (current % 1000) * 1000);
#endif
}

//--------------------------------------------------------------------------------
/**
 * $B=i4|2=(B
 */
void
FrameClock::init()
{
  // $B8=:_;~4V$r<hF@$9$k(B
#if (defined(unix) || defined(__unix__) || defined(_AIX) || defined(__OpenBSD__))
  timeval current;
  gettimeofday(&current, NULL);
  initial_ = FrameTime(current.tv_sec, current.tv_usec);
#elif defined(WIN32)
  LONGLONG cntStart, cntEnd;

  DWORD sampleStart = timeGetTime();
  QueryPerformanceCounter((LARGE_INTEGER *)&cntStart);
  Sleep(1000);
  DWORD sampleEnd = timeGetTime();
  QueryPerformanceCounter((LARGE_INTEGER *)&cntEnd);

  secondPerCounter_ = (sampleEnd - sampleStart) / 1000.0 / (cntEnd - cntStart);
  initial_ = FrameTime((double)cntEnd * secondPerCounter_);
#else
  Uint32 current = SDL_GetTicks();
  initial_ = FrameTime(current / 1000, (current % 1000) * 1000);
#endif

  mode_    = MODE_NORMAL;
}

//--------------------------------------------------------------------------------
/**
 * $BDL>oB.EY$G$NF0:n$r9T$&(B
 */
void
FrameClock::normal()
{
  // $B4{$KDL>oB.EY$N>l9g$K$O=*N;$9$k(B
  if (mode_ == MODE_NORMAL)
  {
    return;
  }
    
  // $B8=:_;~4V$r<hF@$9$k(B
  FrameTime now = __current__() - (initial_ + change_);

  // $B9bB.B.EY$@$C$?>l9g(B
  if (mode_ == MODE_FAST)
  {
    initial_ -= now * (FastTime - 1);
  }
  // $BDcB.B.EY$@$C$?>l9g(B
  else
  {
    initial_ -= now * (SlowTime - 1);
  }

  // $BDL>oB.EY$KLa$9(B
  mode_ = MODE_NORMAL;
}

//--------------------------------------------------------------------------------
/**
 * $B9bB.B.EY$G$NF0:n$r9T$&(B
 */
void
FrameClock::fast()
{
  // $B4{$K9bB.B.EY$N>l9g$K$O=*N;$9$k(B
  if (mode_ == MODE_FAST)
  {
    return;
  }

  // $B8=:_;~4V$r<hF@$9$k(B
  FrameTime now = __current__();

  // $B9bB.$@$C$?>l9g(B
  if (mode_ == MODE_SLOW)
  {
    // $B9bB.B.EY$r3+;O$7$F$+$i$N7P2a;~4V$r5a$a!";~4V$rJd@5$9$k(B
    initial_ -= (now - initial_ - change_) * (SlowTime - 1);
  }

  // $B9bB.B.EY3+;O;~4V$r5-O?$9$k(B
  change_ = now - initial_;

  // $B9bB.B.EY$K0\F0$9$k(B
  mode_   = MODE_FAST;
}

//--------------------------------------------------------------------------------
/**
 * $BDcB.B.EY$G$NF0:n$r9T$&(B
 */
void
FrameClock::slow()
{
  // $B4{$KDcB.B.EY$N>l9g$K$O=*N;$9$k(B
  if (mode_ == MODE_SLOW)
  {
    return;
  }

  // $B8=:_;~4V$r<hF@$9$k(B
  FrameTime now = __current__();

  // $BDcB.$@$C$?>l9g(B
  if (mode_ == MODE_FAST)
  {
    // $BDcB.B.EY$r3+;O$7$F$+$i$N7P2a;~4V$r5a$a!";~4V$rJd@5$9$k(B
    initial_ -= (now - initial_ - change_) * (FastTime - 1);
  }

  // $BDcB.B.EY3+;O;~4V$r5-O?$9$k(B
  change_ = now - initial_;

  // $BDcB.B.EY$K0\F0$9$k(B
  mode_   = MODE_SLOW;
}

//--------------------------------------------------------------------------------
/**
 * 1$B%U%l!<%`$N99?7$r9T$J$&(B
 */
void
FrameClock::update()
{
  // $B8=:_;~4V$r<hF@$9$k(B
  FrameTime now = __current__() - initial_;

  switch(mode_)
  {
  case MODE_NORMAL:
    break;

    // $B9bB.!&DcB.$N>l9g$K$OJd@5$r9T$&(B
  case MODE_FAST:
    now = (now - change_) * FastTime + change_;
    break;

  case MODE_SLOW:
    now = (now - change_) * SlowTime + change_;
    break;
  }

  span_ = now - now_;
  now_  = now;
}

}
